﻿namespace O1.Kernel
{
    internal static class TransactionExtensions
    {
        public const ulong MaxTransactionId = TransactionIdMask;

        private const ulong TransactionIdMask = 0x1FFFFFFFFFFFFFFF;

        private const ulong TransactionIdMaskByte = 0x1F;

        private const ulong DmlTypeMask = ~TransactionIdMask;

        private const ulong DmlTypeMaskByte = ~TransactionIdMaskByte;

        private const int DmlTypeShift = 61;

        private const byte DmlTypeShiftByte = 5;

        private const ulong TransactionStatusMask = 0x7FFFFFFFFFFFFFFF;

        private const byte TransactionStatusMaskByte = 0x7F;

        private const int DmlStatusShift = 63;

        private const byte DmlStatusShiftByte = 7;

        public static int NextCommittedTx(this byte[] changes, int offset, out int size)
        {
            size = 0;
            if (changes == null)
            {
                return -1;
            }

            while (offset < changes.Length)
            {
                var delta = DeltaRecord.Read(changes, offset);
                if (delta.TransactionId.Status() == DmlStatus.Committed)
                {
                    size = delta.DataSize;
                    return offset;
                }

                offset += delta.DataSize;
            }

            return -1;
        }

        public static DmlOperation Operation(this ulong transactionId)
        {
            return (DmlOperation)((transactionId & DmlTypeMask) >> DmlTypeShift);
        }

        public static ulong EmbedOperation(this ulong transactionId, DmlOperation operation)
        {
            return (((ulong)operation) << DmlTypeShift) | (transactionId & TransactionIdMask);
        }

        public static DmlStatus Status(this ulong transactionId)
        {
            return (DmlStatus)(transactionId >> DmlStatusShift);
        }

        public static ulong EmbedStatus(this ulong transactionId, DmlStatus status)
        {
            return (((ulong)status) << DmlStatusShift) | (transactionId & TransactionStatusMask);
        }

        public static ulong ToId(this ulong transactionId)
        {
            return transactionId & TransactionIdMask;
        }

        public static DmlOperation Operation(this byte[] buffer, int offset = 0)
        {
            return (DmlOperation)((buffer[offset] & DmlTypeMaskByte) >> DmlTypeShiftByte);
        }

        public static void EmbedOperation(this byte[] buffer, DmlOperation operation, int offset = 0)
        {
            var value = (byte)operation << DmlTypeShiftByte;
            buffer[offset] = (byte)(value | (byte)(buffer[offset] & TransactionIdMaskByte));
        }

        public static DmlStatus Status(this byte[] buffer, int offset = 0)
        {
            return (DmlStatus)(buffer[offset] >> DmlStatusShiftByte);
        }

        public static void EmbedStatus(this byte[] buffer, DmlStatus status, int offset = 0)
        {
            var value = (byte)status << DmlStatusShiftByte;
            buffer[offset] = (byte)(value | (byte)(buffer[offset] & TransactionStatusMaskByte));
        }

        public static unsafe ulong ToId(this byte[] buffer, int offset = 0)
        {
            fixed (byte* p = &buffer[offset])
            {
                return (*(ulong*)p) & TransactionIdMask;
            }
        }
    }
}
